package in.muton.edushare.components.tasks.electives.allotment;

import in.muton.edushare.components.messaging.api.SystemMessage;
import in.muton.edushare.components.tasks.electives.api.ElectiveUserCompare;
import in.muton.edushare.processing.objects.CourseDataHandler;
import in.muton.edushare.processing.objects.DeptDataHandler;
import in.muton.edushare.wicket.models.CayenneModel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

import com.edushare.common.dao.BatchDAO;
import com.edushare.common.dao.CourseInstancesDAO;
import com.edushare.common.dao.CoursesDAO;
import com.edushare.common.dao.CurriculumDAO;
import com.edushare.common.dao.ElectivePreferenceDAO;
import com.edushare.common.dao.EnrolmentsDAO;
import com.edushare.common.orm.ORMInterface;
import com.edushare.common.vo.Batches;
import com.edushare.common.vo.CourseInstances;
import com.edushare.common.vo.Courses;
import com.edushare.common.vo.Departments;
import com.edushare.common.vo.ElectivePreference;
import com.edushare.common.vo.Enrolments;
import com.edushare.common.vo.SemesterCourses;
import com.edushare.common.vo.Universities;
import com.edushare.common.vo.Users;

public class ElectiveCourseAllot {

	private static final Log log = LogFactory.getLog(ElectiveCourseAllot.class);;

	public ElectiveCourseAllot(IModel<Universities> univModel) {
		this.univModel = univModel;
	}

	public void allotElectiveCourses(ORMInterface orm) {
		BatchDAO bDAO = new BatchDAO(orm);
		List<Batches> batchList = bDAO.getAllBatches(univModel.getObject());
		for (Batches b : batchList) {
			allocateCoursesSem(b, orm);
		}
	}

	public void allocateCoursesSem(Batches b, ORMInterface orm) {
		BatchDAO bDAO = new BatchDAO(orm);
		ElectivePreferenceDAO eDAO = new ElectivePreferenceDAO(orm);
		CurriculumDAO cDAO = new CurriculumDAO(orm);

		int numGroups = cDAO.getNumElectives(b.getCurricula(), b
				.getCurrentSemester());
		List<Users> userList = bDAO.getUsers(b);
		List<Users> autoAllotList = new ArrayList<Users>();
		List<Users> prefAllotList = new ArrayList<Users>();

		for (int i = 0; i < numGroups; i++) {
			autoAllotList = new ArrayList<Users>();
			prefAllotList = new ArrayList<Users>();
			for (Users user : userList) {
				List<SemesterCourses> courseList = cDAO.getElectiveSemCourses(b
						.getCurricula(), b.getCurrentSemester(), i);
				if (eDAO.getPreference(user, courseList.get(0), i) != null) {
					prefAllotList.add(user);
				} else {
					autoAllotList.add(user);
				}
			}
			Collections.sort(prefAllotList, new ElectiveUserCompare(orm, b, i));
			for (Users user : prefAllotList) {
				allotByPreferences(orm, b, user, i);
			}

			for (Users user : autoAllotList) {
				autoAllot(orm, b, user, i);
			}
		}
		orm.commitAllChanges();
	}

	private void autoAllot(ORMInterface orm, Batches b, Users user, int i) {
		CurriculumDAO cDAO = new CurriculumDAO(orm);
		log.debug("Inside auto allot");
		List<SemesterCourses> courseList = cDAO.getElectiveSemCourses(b
				.getCurricula(), b.getCurrentSemester(), i);
		Departments d = b.getCurricula().getDepartment();
		DeptDataHandler ddh = new DeptDataHandler(
				new CayenneModel<Departments>(d));
		if (!autoAllotElective(orm, courseList, user, i))
			new SystemMessage(orm, user, d, "Unable to allocate elective " + i
					+ " to student " + user.getCompleteUserName(), ddh
					.getPrimaryAdmins()).send();
	}

	private boolean autoAllotElective(ORMInterface orm,
			List<SemesterCourses> courseList, Users user, int i) {
		EnrolmentsDAO enrolDAO = new EnrolmentsDAO(orm);
		CourseInstancesDAO courseDAO = new CourseInstancesDAO(orm);
		CoursesDAO crsDAO = new CoursesDAO(orm);
		for (SemesterCourses s : courseList) {
			CourseInstances c = courseDAO.getActiveInst(s.getCourse());
			if (c == null) {
				c = courseDAO.newBatchInst(s.getCourse());
			}
			if (!crsDAO.hasUserCompletedCourse(user, c.getCourse())
					&& crsDAO.checkPrerequisites(c.getCourse(), user)) {
				log.debug("User satisfied prereq for course "
						+ c.getCourse().getCourseName());
				int sanctionedStrength = c.getCourse().getStrength();
				if (new CourseDataHandler(new Model<CourseInstances>(c))
						.getStudents().getObject().size() < sanctionedStrength
						|| sanctionedStrength == Courses.NOT_AVAILABLE) {
					Enrolments e = enrolDAO
							.addUser(c, user, Enrolments.STUDENT);
					e.setStatus(Enrolments.ENROLLED);
					return true;
				}
			}
		}
		return false;
	}

	private void allotByPreferences(ORMInterface orm, Batches b, Users user,
			int i) {
		ElectivePreferenceDAO eDAO = new ElectivePreferenceDAO(orm);
		CurriculumDAO cDAO = new CurriculumDAO(orm);
		log.debug("Inside preferential allot");
		List<SemesterCourses> courseList = cDAO.getElectiveSemCourses(b
				.getCurricula(), b.getCurrentSemester(), i);
		List<ElectivePreference> ePrefList = new ArrayList<ElectivePreference>();
		for (SemesterCourses c : courseList) {
			ePrefList.add(eDAO.getPreference(user, c, i));
		}
		Departments d = b.getCurricula().getDepartment();
		DeptDataHandler ddh = new DeptDataHandler(
				new CayenneModel<Departments>(d));
		sortInOrder(ePrefList);
		if (!allotByPreferences(orm, ePrefList, user, i))
			new SystemMessage(orm, user, d, "Unable to allocate elective " + i
					+ " to student " + user.getCompleteUserName(), ddh
					.getPrimaryAdmins()).send();
	}

	private boolean allotByPreferences(ORMInterface orm,
			List<ElectivePreference> ePrefList, Users user, int i) {
		CourseInstancesDAO courseDAO = new CourseInstancesDAO(orm);
		CoursesDAO crsDAO = new CoursesDAO(orm);
		EnrolmentsDAO enrolDAO = new EnrolmentsDAO(orm);
		for (ElectivePreference ePref : ePrefList) {
			CourseInstances c = courseDAO.getActiveInst(ePref.getElective()
					.getCourse());
			if (c == null) {
				c = courseDAO.newBatchInst(ePref.getElective().getCourse());
			}

			log.debug("looking at preference " + ePref.getPreferenceNumber());

			if (!crsDAO.hasUserCompletedCourse(user, c.getCourse())
					&& crsDAO.checkPrerequisites(c.getCourse(), user)) {
				int sanctionedStrength = c.getCourse().getStrength();
				if (new CourseDataHandler(new Model<CourseInstances>(c))
						.getStudents().getObject().size() < sanctionedStrength
						|| sanctionedStrength == Courses.NOT_AVAILABLE) {
					Enrolments e = enrolDAO
							.addUser(c, user, Enrolments.STUDENT);
					e.setStatus(Enrolments.ENROLLED);
					return true;
				}
			}
		}
		return false;
	}

	private void sortInOrder(List<ElectivePreference> prefList) {
		Collections.sort(prefList, PREFERENCE);
	}

	private static final Comparator<ElectivePreference> PREFERENCE = new Comparator<ElectivePreference>() {
		public int compare(ElectivePreference e1, ElectivePreference e2) {
			return e1.getPreferenceNumber().compareTo(e2.getPreferenceNumber());
		}
	};

	private IModel<Universities> univModel;
}
